package org.mirrentools.attendance.console.service.impl;

import org.mirrentools.attendance.console.common.FutureUtil;
import org.mirrentools.attendance.console.common.ResultCodeEnum;
import org.mirrentools.attendance.console.common.StringUtil;
import org.mirrentools.attendance.console.entity.Record;
import org.mirrentools.attendance.console.service.RecordService;
import org.mirrentools.attendance.console.service.UidUtiil;
import org.mirrentools.attendance.console.sql.RecordSQL;
import io.vertx.core.*;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.file.FileSystem;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.jdbc.JDBCClient;
import io.vertx.ext.sql.assist.SQLExecute;
import io.vertx.ext.sql.assist.SqlAndParams;
import io.vertx.ext.sql.assist.SqlAssist;
import io.vertx.ext.web.RoutingContext;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Date;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * Record数据服务接口的默认实现
 */
public class RecordServiceImpl implements RecordService {
  /**
   * 日志工具
   */
  private final Logger LOG = LoggerFactory.getLogger(this.getClass());
  /**
   * SQL操作语句
   */
  private final RecordSQL recordSql;
  private final String userDir;

  /**
   * 初始化
   */
  public RecordServiceImpl(SQLExecute<JDBCClient> execute) {
    super();
    this.recordSql = new RecordSQL(execute);
    this.userDir = System.clearProperty("user.dir");
  }

  @Override
  public void findCalendarGroup(MultiMap params, Handler<AsyncResult<JsonObject>> handler) {
    String sql = "SELECT CONCAT(years,'-',months) AS m FROM calendar GROUP BY years,months ORDER BY years DESC,months DESC  LIMIT 200 ";
    recordSql.queryAsListObj(new SqlAndParams(sql), res -> {
      if (res.succeeded()) {
        List<JsonObject> items = res.result() == null ? new ArrayList<>() : res.result();
        if (LOG.isDebugEnabled()) {
          LOG.debug("执行获取行事历分类-->结果:" + res.result());
        }
        handler.handle(FutureUtil.successC200(items));
      } else {
        String message = res.cause().getMessage();
        LOG.error("执行获取行事历分类-->失败:", res.cause());
        handler.handle(FutureUtil.successC500Zero("获取行事历以及用户的考勤,请稍后重试!", message));
      }
    });
  }

  @Override
  public void findCalendar(MultiMap params, Handler<AsyncResult<JsonObject>> handler) {
    Integer year = StringUtil.getInteger(params.get("year"));
    Integer month = StringUtil.getInteger(params.get("month"));
    String uid = params.get("uid");
    if (!StringUtil.isNullOrEmpty(params.get("finalUid"))) {
      uid = params.get("finalUid");
    }
    if (StringUtil.isNullOrEmpty(uid, year, month)) {
      handler.handle(FutureUtil.successZero(ResultCodeEnum.C412));
      return;
    }
    String sql = "SELECT c.years AS ys,c.months AS ms,c.days AS ds,c.workd AS w,COALESCE(r.state,-1) AS u FROM calendar c " +
            " LEFT JOIN report r ON (r.uid=? AND r.years=c.years AND r.months=c.months AND r.days=c.days) " +
            " WHERE c.years=? AND c.months=? ORDER BY c.days";
    JsonArray sqldata = new JsonArray().add(uid).add(year).add(month);
    recordSql.queryAsListObj(new SqlAndParams(sql, sqldata), res -> {
      if (res.succeeded()) {
        List<JsonObject> items = res.result() == null ? new ArrayList<>() : res.result();
        if (LOG.isDebugEnabled()) {
          LOG.debug("执行获取行事历以及用户的考勤-->结果:" + res.result());
        }
        handler.handle(FutureUtil.successC200(items));
      } else {
        String message = res.cause().getMessage();
        LOG.error("执行获取行事历以及用户的考勤-->失败:", res.cause());
        handler.handle(FutureUtil.successC500Zero("获取行事历以及用户的考勤,请稍后重试!", message));
      }
    });
  }

  @Override
  public void findDays(MultiMap params, Handler<AsyncResult<JsonObject>> handler) {
    String uid;
    if (!StringUtil.isNullOrEmpty(params.get("finalUid"))) {
      uid = params.get("finalUid");
    } else {
      uid = params.get("uid");
    }
    if (StringUtil.isNullOrEmpty(uid)) {
      handler.handle(FutureUtil.successZero(ResultCodeEnum.C301));
      return;
    }
    LocalDate date = LocalDate.now();
    int year = StringUtil.getInteger(params.get("year"), date.getYear());
    int month; //兼容错别字mouth
    if (StringUtil.isNullOrEmpty(params.get("month"))) {
      month = StringUtil.getInteger(params.get("mouth"), date.getMonthValue());
    } else {
      month = StringUtil.getInteger(params.get("month"), date.getMonthValue());
    }
    int day = StringUtil.getInteger(params.get("day"), date.getDayOfMonth());
    Promise<JsonObject> promise = Promise.promise();
    promise.future().onComplete(data -> {
      if (data.succeeded()) {
        JsonObject result = data.result() == null ? new JsonObject() : data.result();
        if (LOG.isDebugEnabled()) {
          LOG.debug("执行获取每小时打卡数据->获取汇报与时长-->结果:" + result);
        }
        if (result.isEmpty()) {
          result.putNull("report");
          result.put("wtime", 0);
        }
        SqlAssist assist = new SqlAssist();
        assist.andEq("years", year).andEq("months", month).andEq("days", day).andEq("uid", uid);
        assist.setOrders(SqlAssist.order("id", false));
        assist.setResultColumn("FROM_UNIXTIME(start/1000,'%H:%i:%s') as start,FROM_UNIXTIME(end/1000,'%H:%i:%s') as end,wtime");
        recordSql.selectAll(assist, res -> {
          if (res.succeeded()) {
            List<JsonObject> items = res.result() == null ? new ArrayList<>() : res.result();
            if (LOG.isDebugEnabled()) {
              LOG.debug("执行获取每小时打卡数据-->结果:" + res.result());
            }
            result.put("items", items);
            handler.handle(FutureUtil.successC200(result));
          } else {
            String message = res.cause().getMessage();
            LOG.error("执行获取每小时打卡数据-->失败:", res.cause());
            handler.handle(FutureUtil.successC500Zero("操作获取打卡数据,请稍后重试!", message));
          }
        });
      } else {
        String message = data.cause().getMessage();
        LOG.error("执行获取每小时打卡数据->获取汇报与时长-->失败:", data.cause());
        handler.handle(FutureUtil.successC500Zero("获取汇报与时长,请稍后重试!", message));
      }
    });
    // 获取汇报信息与时长
    SqlAndParams qp = new SqlAndParams(
            "SELECT SUM(wtime) AS wtime,(SELECT body FROM report WHERE id=? ) AS report,(SELECT state FROM report WHERE id=? ) AS state   FROM record   WHERE years = ? AND months = ? AND days = ? AND uid = ?");
    qp.setParams(new JsonArray().add(UidUtiil.uid(uid, year, month, day)).add(UidUtiil.uid(uid, year, month, day)).add(year).add(month)
            .add(day).add(uid));
    recordSql.queryAsObj(qp, promise);
  }

  @Override
  public void findMonths(MultiMap params, Handler<AsyncResult<JsonObject>> handler) {
    String uid = params.get("uid");
    if (!StringUtil.isNullOrEmpty(params.get("finalUid"))) {
      uid = params.get("finalUid");
    }
    if (StringUtil.isNullOrEmpty(uid)) {
      handler.handle(FutureUtil.successZero(ResultCodeEnum.C301));
      return;
    }
    LocalDate date = LocalDate.now();
    int year = StringUtil.getInteger(params.get("year"), date.getYear());
    int month = StringUtil.getInteger(params.get("month"), date.getMonthValue());
    SqlAndParams qp = new SqlAndParams(
            "SELECT (SELECT COUNT(*) FROM (SELECT r.uid,r.years,r.months,r.days FROM record r WHERE r.years=? AND r.months=? AND r.uid=? GROUP BY uid,years,months,days ) t) AS days,"
                    + "(SELECT COUNT(*) FROM `calendar` WHERE years=? AND months=? AND workd=1) AS workd,"
                    + "SUM(wtime) AS wtime FROM record WHERE years=? AND months=? AND uid=?");
    qp.setParams(new JsonArray().add(year).add(month).add(uid).add(year).add(month).add(year).add(month).add(uid));
    String finalUid = uid;
    recordSql.queryAsObj(qp, rec -> {
      if (rec.succeeded()) {
        JsonObject result = rec.result() == null ? new JsonObject() : rec.result();
        if (LOG.isDebugEnabled()) {
          LOG.debug("执行获取每月考勤->获取累计信息-->结果:" + result);
        }
        if (result.isEmpty()) {
          result.put("days", 0);
          result.put("workd", 0);
          result.put("wtime", 0);
        }
        result.put("year", year);
        result.put("month", month);
        SqlAndParams recordQP = new SqlAndParams(
                "SELECT r.days, sum(wtime) AS wtime FROM record r WHERE r.years = ? AND r.months = ? AND r.uid = ? group by days");
        recordQP.setParams(new JsonArray().add(year).add(month).add(finalUid));
        recordSql.queryAsListObj(recordQP, res -> {
          if (res.succeeded()) {
            List<JsonObject> recordItems = res.result() == null ? new ArrayList<>() : res.result();
            if (LOG.isDebugEnabled()) {
              LOG.debug("执行获取每月考勤数据-->结果:" + res.result());
            }
            result.put("records", recordItems);
            SqlAndParams reportQP = new SqlAndParams("SELECT days,state,body AS report FROM report WHERE years = ? AND  months = ? AND uid = ?");
            reportQP.setParams(new JsonArray().add(year).add(month).add(finalUid));
            recordSql.queryAsListObj(reportQP, rep -> {
              if (rep.succeeded()) {
                List<JsonObject> reportItems = rep.result() == null ? new ArrayList<>() : rep.result();
                if (LOG.isDebugEnabled()) {
                  LOG.debug("执行获取每月汇报数据数据-->结果:" + rep.result());
                }
                result.put("reports", reportItems);
                handler.handle(FutureUtil.successC200(result));
              } else {
                String message = rep.cause().getMessage();
                LOG.error("执行获取每月汇报数据数据-->失败:", rep.cause());
                handler.handle(FutureUtil.successC500Zero("操作获取汇报数据,请稍后重试!", message));
              }
            });
          } else {
            String message = res.cause().getMessage();
            LOG.error("执行获取每月考勤数据-->失败:", res.cause());
            handler.handle(FutureUtil.successC500Zero("操作获取打卡数据,请稍后重试!", message));
          }
        });
      } else {
        String message = rec.cause().getMessage();
        LOG.error("执行获取每月考勤->获取累计信息-->失败:", rec.cause());
        handler.handle(FutureUtil.successC500Zero("获取每月考勤记录,请稍后重试!", message));
      }
    });
  }

  @Override
  public void findCounts(MultiMap params, Handler<AsyncResult<JsonObject>> handler) {
    Long selectMonth = StringUtil.getLong(params.get("selectMonth"));
    LocalDate date = selectMonth == null ? LocalDate.now() : new Date(selectMonth).toLocalDate();
    int year = date.getYear();
    int month = date.getMonthValue();
    SqlAndParams qp = new SqlAndParams(
            "SELECT id,nickname," + "(SELECT COALESCE(SUM(wtime),0) FROM record WHERE years=? AND months=? AND uid=users.id) AS wtime,"
                    + "(SELECT COUNT(DISTINCT days) FROM  record WHERE years=? AND months=? AND uid=users.id) AS days  FROM users");
    qp.setParams(new JsonArray().add(year).add(month).add(year).add(month));
    recordSql.queryAsListObj(qp, res -> {
      if (res.succeeded()) {
        List<JsonObject> result = res.result() == null ? new ArrayList<>() : res.result();
        if (LOG.isDebugEnabled()) {
          LOG.debug("执行获取考勤统计信息-->结果:" + res.result());
        }
        handler.handle(FutureUtil.successC200(result));
      } else {
        String message = res.cause().getMessage();
        LOG.error("执行获取考勤统计信息-->失败:", res.cause());
        handler.handle(FutureUtil.successC500Zero("获取考勤统计,请稍后重试!", message));
      }
    });
  }

  @Override
  public void getLastWork(MultiMap params, Handler<AsyncResult<JsonObject>> handler) {
    String uid = params.get("uid");
    if (StringUtil.isNullOrEmpty(uid)) {
      handler.handle(FutureUtil.successZero(ResultCodeEnum.C301));
      return;
    }
    LocalDate date = LocalDate.now();
    int year = date.getYear();
    int month = date.getMonthValue();
    int day = date.getDayOfMonth();
    SqlAssist assist = new SqlAssist();
    assist.andEq("years", year).andEq("months", month).andEq("days", day).andEq("uid", uid);
    assist.setRowSize(1);
    assist.setOrders(SqlAssist.order("id", false));
    recordSql.selectAll(assist, res -> {
      if (res.succeeded()) {
        List<JsonObject> items = res.result() == null ? new ArrayList<>() : res.result();
        if (LOG.isDebugEnabled()) {
          LOG.debug("执行最后一次打卡信息-->结果:" + res.result());
        }
        if (items.size() > 0) {
          handler.handle(FutureUtil.successC200(items.get(0)));
        } else {
          handler.handle(FutureUtil.successC200NewJson());
        }
      } else {
        String message = res.cause().getMessage();
        LOG.error("执行最后一次打卡信息-->失败:", res.cause());
        handler.handle(FutureUtil.successC500Zero("获取最后一次打卡信息,请稍后重试!", message));
      }
    });

  }

  @Override
  public void work(MultiMap params, Handler<AsyncResult<JsonObject>> handler) {
    String uid = params.get("uid");
    if (StringUtil.isNullOrEmpty(uid)) {
      handler.handle(FutureUtil.successZero(ResultCodeEnum.C301));
      return;
    }
    LocalDate date = LocalDate.now();
    int year = date.getYear();
    int month = date.getMonthValue();
    int day = date.getDayOfMonth();
    Record record = new Record();
    record.setUid(uid).setYears(year).setMonths(month).setDays(day).setStart(System.currentTimeMillis());
    recordSql.insertNonEmpty(record, res -> {
      if (res.succeeded()) {
        if (LOG.isDebugEnabled()) {
          LOG.debug("执行工作打卡-->结果:" + res.result());
        }
        if (res.result() > 0) {
          handler.handle(FutureUtil.successC200One());
        } else {
          handler.handle(FutureUtil.successC200Zero());
        }
      } else {
        String message = res.cause().getMessage();
        LOG.error("执行工作打卡-->失败:", res.cause());
        handler.handle(FutureUtil.successC500Zero("执行工作打卡,请稍后重试!", message));
      }
    });
  }

  @Override
  public void rest(MultiMap params, Handler<AsyncResult<JsonObject>> handler) {
    Long id = StringUtil.getLong(params.get("id"));
    if (id == null) {
      handler.handle(FutureUtil.successC412Zero("休息打卡失败,工作id不能为空"));
      return;
    }
    SqlAndParams qp = new SqlAndParams("update record set end=?,wtime=(?-start) where id=?");
    long time = System.currentTimeMillis();
    qp.setParams(new JsonArray().add(time).add(time).add(id));
    recordSql.update(qp, res -> {
      if (res.succeeded()) {
        if (LOG.isDebugEnabled()) {
          LOG.debug("执行休息打卡-->结果:" + res.result());
        }
        if (res.result() > 0) {
          handler.handle(FutureUtil.successC200One());
        } else {
          handler.handle(FutureUtil.successC500Zero("执行休息打卡失败,请刷新当前页面后重试!"));
        }
      } else {
        String message = res.cause().getMessage();
        LOG.error("执行休息打卡-->失败:", res.cause());
        handler.handle(FutureUtil.successC500Zero("执行休息打卡,请稍后重试!", message));
      }

    });
  }

  @Override
  public void uploadImage(RoutingContext rct, MultiMap params, Handler<AsyncResult<JsonObject>> handler) {
    String uid = params.get("uid");
    Buffer body = rct.getBody();
    if (StringUtil.isNullOrEmpty(uid) || body == null) {
      handler.handle(FutureUtil.successC412Zero("图片不能为空!"));
      return;
    }
    try {
      LocalDateTime now = LocalDateTime.now();
      String imageName = DateTimeFormatter.ofPattern("HHmmSSS").format(now) + ".png";
      String yearMonth = now.getYear() + "-" + now.getMonthValue();
      String day = Integer.toString(now.getDayOfMonth());
      String root = String.format("%s/screenshot/%s/%s/%s/", userDir, uid, yearMonth, day);
      if (Files.notExists(Paths.get(root))) {
        Files.createDirectories(Paths.get(root));
      }
      Vertx vertx = rct.vertx();
      FileSystem fs = vertx.fileSystem();
      fs.writeFile(root + imageName, body, res -> {
        if (res.succeeded()) {
          handler.handle(FutureUtil.successC200One());
        } else {
          LOG.error("执行保存图片-->失败:", res);
          handler.handle(FutureUtil.successC500Zero("服务器保存图片失败,请联系管理员!"));
        }
      });
    } catch (Exception e) {
      LOG.error("执行保存图片-->失败:", e);
      handler.handle(FutureUtil.successC500Zero("服务器保存图片失败,请联系管理员!"));
    }
  }
}
